#include "GLRenderer.h"

namespace simple_renderer {

GLRenderer::GLRenderer() :
                           vertex_shader(0),
                           fragment_shader(0),
                           program(0),
                           _renderer_inited(false){
    memset(plane_textures, 0, sizeof(plane_textures) * GLES2_MAX_PLANE);
}

GLRenderer::~GLRenderer(){

}

int GLRenderer::InitRenderer(){

    std::unique_lock<std::mutex> lck(_init_mux);
    if(_renderer_inited)
        return 0;

    const char *fragment_shader_str = GetFragmentShader();
    int ret = InitBase(fragment_shader_str);
    if(ret != 0) {
        LOGE("[GLRenderer] InitRenderer InitBase error ret=%d", ret);
        return ret;
    }
    ret = InitExtend();
    if(ret != 0) {
        LOGE("[GLRenderer] InitRenderer InitExtend error ret=%d", ret);
        return ret;
    }
    LOGI("[GLRenderer] InitRenderer ok");
    ret = UseProgram();
    LOGI("[GLRenderer] UseProgram ret=%d", ret);

    GLES_Matrix modelViewProj;
    //project matrix
    GLES2_loadOrtho(&modelViewProj, -1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
    glUniformMatrix4fv(um4_mvp, 1, GL_FALSE, modelViewProj.m);                    GLES2_checkError("glUniformMatrix4fv(um4_mvp)");

    ResetTexCoords();
    ReloadTexCoords();

    ResetVertices();
    ReloadVertices();

    _renderer_inited = true;

    return ret;
}

void GLRenderer::DrawPicFrame(const PicFrame &pic_frame, int win_w, int win_h){

    DrawFrame(pic_frame, win_w, win_h);
}

int GLRenderer::ReleaseRenderer(){

    std::unique_lock<std::mutex> lck(_init_mux);
    int ret = ReleaseBase();
    _renderer_inited = false;

    return ret;
}

int GLRenderer::InitBase(const char* fragment_shader_str){

    GLint link_status = GL_FALSE;

    vertex_shader = GLES2_loadShader(GL_VERTEX_SHADER, GLES2_getVertexShader_default());
    if(!vertex_shader)
        goto fail;
    fragment_shader = GLES2_loadShader(GL_FRAGMENT_SHADER, fragment_shader_str);
    if(!fragment_shader)
        goto fail;
    program = glCreateProgram();   GLES2_checkError("glCreateProgram");
    if(!program)
        goto fail;

    glAttachShader(program, vertex_shader);    GLES2_checkError("glAttachShader(vertex)");
    glAttachShader(program, fragment_shader);   GLES2_checkError("glAttachShader(fragment)");
    glLinkProgram(program);     GLES2_checkError("glCreateProgram");
    glGetProgramiv(program, GL_LINK_STATUS, &link_status);
    if(!link_status)
        goto fail;

    av4_position = glGetAttribLocation(program, "av4_Position");    GLES2_checkError("glGetAttribLocation(av4_Position)");
    av2_texcoord = glGetAttribLocation(program, "av2_Texcoord");    GLES2_checkError("glGetAttribLocation(av2_Texcoord)");
    um4_mvp = glGetUniformLocation(program, "um4_ModelViewProjection");     GLES2_checkError("glGetUniformLocation(um4_ModelViewProjection)");

    LOGI("[GLRenderer] InitBase ok");

    return 0;
fail:
    if(program)
        GLES2_printProgramInfo(program);

    ReleaseBase();
    return -1;
}

int GLRenderer::ReleaseBase(){

    if(vertex_shader){
        glDeleteShader(vertex_shader);
        vertex_shader = 0;
    }
    if(fragment_shader){
        glDeleteShader(fragment_shader);
        fragment_shader = 0;
    }
    if(program){
        glDeleteProgram(program);
        program = 0;
    }

    for(int i=0; i<GLES2_MAX_PLANE; i++){
        if(plane_textures[i]){
            glDeleteTextures(1, &plane_textures[i]);
            plane_textures[i] = 0;
        }
    }
    return 0;
}

int GLRenderer::DrawFrame(const PicFrame &pic_frame, int win_w, int win_h){

    glClear(GL_COLOR_BUFFER_BIT);               GLES2_checkError("glClear");

    LOGV("[GLRenderer] DrawFrame window_w=%d window_h=%d", win_w, win_h);
    //glViewport(0, 0, win_w, win_h);  GLES2_checkError("glViewport");

    int ret = UploadTexture(pic_frame);
    if(ret == 0) {
        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
        GLES2_checkError("glDrawArrays");
    }

    return ret;
}

void GLRenderer::ClearWindow(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha){

    glClearColor(red, green, blue, alpha);
    for (GLint error = glGetError(); error; error = glGetError()) {
        LOGE("[GLES2] after %s() glError (0x%x)\n", "glClearColor", error);
    }
    glEnable(GL_CULL_FACE);
    for (GLint error = glGetError(); error; error = glGetError()) {
        LOGE("[GLES2] after %s() glError (0x%x)\n", "glEnable", error);
    }
    glCullFace(GL_BACK);
    for (GLint error = glGetError(); error; error = glGetError()) {
        LOGE("[GLES2] after %s() glCullFace (0x%x)\n", "glCullFace", error);
    }
    glDisable(GL_DEPTH_TEST);
    for (GLint error = glGetError(); error; error = glGetError()) {
        LOGE("[GLES2] after %s() glDisable (0x%x)\n", "glEnable", error);
    }
    glClear(GL_COLOR_BUFFER_BIT);
    for (GLint error = glGetError(); error; error = glGetError()) {
        LOGE("[GLES2] after %s() glDisable (0x%x)\n", "glClear", error);
    }
}

void GLRenderer::ResetTexCoords(){
    texcoords[0] = 0.0f;
    texcoords[1] = 1.0f;
    texcoords[2] = 1.0f;
    texcoords[3] = 1.0f;
    texcoords[4] = 0.0f;
    texcoords[5] = 0.0f;
    texcoords[6] = 1.0f;
    texcoords[7] = 0.0f;
}

void GLRenderer::ReloadTexCoords(){

    glVertexAttribPointer(av2_texcoord, 2, GL_FLOAT, GL_FALSE, 0, texcoords);   GLES2_checkError("glVertexAttribPointer(av2_texcoord)");
    glEnableVertexAttribArray(av2_texcoord);                                              GLES2_checkError("glEnableVertexAttribArray(av2_texcoord)");
}

void GLRenderer::ResetVertices(){

    vertices[0] = -1.0f;
    vertices[1] = -1.0f;
    vertices[2] =  1.0f;
    vertices[3] = -1.0f;
    vertices[4] = -1.0f;
    vertices[5] =  1.0f;
    vertices[6] =  1.0f;
    vertices[7] =  1.0f;
}

void GLRenderer::GLES2_loadOrtho(GLES_Matrix *matrix, GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, GLfloat near, GLfloat far)
{
    GLfloat r_l = right - left;
    GLfloat t_b = top - bottom;
    GLfloat f_n = far - near;
    GLfloat tx = - (right + left) / (right - left);
    GLfloat ty = - (top + bottom) / (top - bottom);
    GLfloat tz = - (far + near) / (far - near);

    matrix->m[0] = 2.0f / r_l;
    matrix->m[1] = 0.0f;
    matrix->m[2] = 0.0f;
    matrix->m[3] = 0.0f;

    matrix->m[4] = 0.0f;
    matrix->m[5] = 2.0f / t_b;
    matrix->m[6] = 0.0f;
    matrix->m[7] = 0.0f;

    matrix->m[8] = 0.0f;
    matrix->m[9] = 0.0f;
    matrix->m[10] = -2.0f / f_n;
    matrix->m[11] = 0.0f;

    matrix->m[12] = tx;
    matrix->m[13] = ty;
    matrix->m[14] = tz;
    matrix->m[15] = 1.0f;
}

void GLRenderer::ReloadVertices(){

    glVertexAttribPointer(av4_position, 2, GL_FLOAT, GL_FALSE, 0, vertices);    GLES2_checkError("glVertexAttribPointer(av2_texcoord)");
    glEnableVertexAttribArray(av4_position);                                      GLES2_checkError("glEnableVertexAttribArray(av2_texcoord)");
}

}